package com.ruoyi.iot.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;

import com.ruoyi.iot.domain.*;
import com.ruoyi.iot.domain.vo.IotModbusAttributeOperateParameters;
import com.ruoyi.iot.domain.vo.ModbusReadRequest;
import com.ruoyi.iot.enums.ModbusRegisterTypeEnum;
import com.ruoyi.iot.modbus.rtu.Modbus4jRTUReadUtils;
import com.ruoyi.iot.modbus.rtu.RTUMasterUtils;
import com.ruoyi.iot.modbus.tcp.CreateTCPMasterUtils;
import com.ruoyi.iot.service.IIotDeviceDataService;
import com.ruoyi.iot.utils.ModbusCache;
import com.ruoyi.iot.utils.SnowUtil;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.ip.IpParameters;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * Modbus设备取数
 *
 * @Author: Howe
 * @Date: 2025-03-07 13:21
 */
@Slf4j
@Service
public class IotModbusService {


    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IIotDeviceDataService deviceDataService;


    /**
     * 获取modbus设备寄存器的地址的值
     *
     * @Param: null
     * @Param: null
     * @Return:
     * @Author: Howe
     * @Date: 2025/3/7 下午1:25
     **/
    public void getDeviceData(IotMasterConfig masterConfig) {
        IotProductConfig productConfig = redisCache.getCacheObject(CacheConstants.PRODUCT_KEY + masterConfig.getProductId());
        if (productConfig == null) {
            return;
        }
        //todo：根据产品数据协议创建采集的master，目前只使用tcp的master
        List<IotDevice> iotDevices = redisCache.getCacheList(CacheConstants.DEVICE_LIST_KEY + masterConfig.getProductId());
        if (CollUtil.isEmpty(iotDevices)) {
            return;
        }
        List<IotProductAttributeModel> iotProductAttributeModels = redisCache.getCacheList(CacheConstants.ATTRIBUTE_LIST_KEY + masterConfig.getProductId());
        if (CollUtil.isEmpty(iotProductAttributeModels)) {
            return;
        }
        for (IotDevice iotDevice : iotDevices) {
            Long collectId = SnowUtil.nextId();
            ModbusMaster master = getTCPModbusMaster(iotDevice.getGatewayIp(), iotDevice.getGatewayPort());
            List<IotDeviceData> iotDeviceDataList = new ArrayList<>();
            for (IotProductAttributeModel iotProductAttributeModel : iotProductAttributeModels) {
                //获取modbus读取对象
                ModbusReadRequest modbusReadRequest = getModbusReadRequest(iotDevice, iotProductAttributeModel, master);
                //去寄存器上读取寄存器的值
                String responseValue = readRegister(modbusReadRequest);
                if (responseValue == null) {
                    log.error("读取寄存器失败，设备：{}，寄存器：{}", iotDevice.getDeviceCode(), iotProductAttributeModel.getModbusRegister());
                    continue;
                }
                //组装设备上报数据对象
                IotDeviceData iotDeviceData = getDeviceData(productConfig, iotDevice, iotProductAttributeModel, responseValue);
                //插入采集批次
                iotDeviceData.setCollectId(collectId);
                iotDeviceDataList.add(iotDeviceData);
                try {
                    Thread.sleep(masterConfig.getIntervalTime());
                } catch (InterruptedException e) {
                    log.error("线程中断异常：{}", e.getMessage());
                }
            }
            //保存数据
            deviceDataService.batchInsertIotDeviceData(iotDeviceDataList);
        }

    }

    /**
     * 获取modbus读取对象
     *
     * @Param: null
     * @Return:
     * @Author: Howe
     * @Date: 2025/3/7 下午4:35
     **/
    private ModbusReadRequest getModbusReadRequest(IotDevice iotDevice, IotProductAttributeModel iotProductAttributeModel, ModbusMaster master) {
        ModbusReadRequest modbusReadRequest = new ModbusReadRequest();
        modbusReadRequest.setReadCode(ModbusRegisterTypeEnum.getReadCodeByType(iotProductAttributeModel.getModbusRegisterType()));
        modbusReadRequest.setType(iotProductAttributeModel.getModbusDataType());
        modbusReadRequest.setMaster(master);
        modbusReadRequest.setStart(iotProductAttributeModel.getModbusRegister());
        modbusReadRequest.setLen(iotProductAttributeModel.getRegisterCount());
        modbusReadRequest.setSlaveId(iotDevice.getMachineCode());
        return modbusReadRequest;
    }


    /**
     * 获取设备上报数据对象
     *
     * @Param: productConfig, iotDevice, iotProductAttributeModel, responseValue
     * @Return: com.ruoyi.iot.IotDeviceData
     * @Author: Howe
     * @Date: 2025/3/7 下午6:59
     **/
    public IotDeviceData getDeviceData(IotProductConfig productConfig, IotDevice iotDevice, IotProductAttributeModel iotProductAttributeModel, String responseValue) {

        IotDeviceData iotDeviceData = new IotDeviceData();
        iotDeviceData.setProductCode(productConfig.getProductCode());
        iotDeviceData.setProductName(productConfig.getProductName());
        iotDeviceData.setDeviceId(iotDevice.getDeviceId());
        iotDeviceData.setDeviceCode(iotDevice.getDeviceCode());
        iotDeviceData.setDeviceName(iotDevice.getDeviceName());
        iotDeviceData.setAttributeCode(iotProductAttributeModel.getAttributeCode());
        iotDeviceData.setAttributeName(iotProductAttributeModel.getAttributeName());
        iotDeviceData.setAttributeUnit(iotProductAttributeModel.getUnit());
        iotDeviceData.setAttributeType(iotProductAttributeModel.getAttributeType());
        if (responseValue != null) {
            // responseValue 乘以属性中的缩放因子,并保留两位小数
            BigDecimal dataValue = new BigDecimal(responseValue).multiply(BigDecimal.valueOf(iotProductAttributeModel.getScaleFactor())).setScale(2, RoundingMode.HALF_UP);
            iotDeviceData.setDataValue(String.valueOf(dataValue));
        }

        iotDeviceData.setReportTime(LocalDateTime.now());
        iotDeviceData.setCreateTime(DateUtils.getNowDate());
        iotDeviceData.setDelFlag("0");
        return iotDeviceData;
    }


    /**
     * 获取modbus设备master
     *
     * @Param: ip, port
     * @Return: ModbusMaster
     * @Author: Howe
     * @Date: 2025/3/7 下午2:11
     **/
    public ModbusMaster getTCPModbusMaster(String ip, Integer port) {
        ModbusMaster master = ModbusCache.getModbusMasterMap().get(ip);
        if (master == null) {
            IpParameters params = new IpParameters();
            params.setHost(ip);
            params.setPort(port);
            params.setEncapsulated(true);
            master = CreateTCPMasterUtils.getTCPMaster(params);//TCP协议
            ModbusCache.getModbusMasterMap().put(ip, master);
        }
        return master;
    }


    /**
     * 读寄存器
     *
     * @Param: modbusReadRequest
     * @Return: java.lang.String
     * @Author: Howe
     * @Date: 2025/3/7 下午6:59
     **/
    public String readRegister(ModbusReadRequest modbusReadRequest) {

        log.info("开始读取寄存器属性{}", JSONUtil.toJsonStr(modbusReadRequest));
        String responseValue = null;
        switch (modbusReadRequest.getReadCode()) {
            case "01":
                responseValue = Modbus4jRTUReadUtils.readCoil(modbusReadRequest.getMaster(), modbusReadRequest.getSlaveId(), modbusReadRequest.getStart(), modbusReadRequest.getLen());
                break;
            case "02":
                responseValue = Modbus4jRTUReadUtils.readDiscreteInput(modbusReadRequest.getMaster(), modbusReadRequest.getSlaveId(), modbusReadRequest.getStart(), modbusReadRequest.getLen());
                break;
            case "03":
                responseValue = Modbus4jRTUReadUtils.readHoldingRegisters(modbusReadRequest.getMaster(), modbusReadRequest.getSlaveId(), modbusReadRequest.getStart(), modbusReadRequest.getLen(), modbusReadRequest.getType());
                break;
            case "04":
                responseValue = Modbus4jRTUReadUtils.readInputRegisters(modbusReadRequest.getMaster(), modbusReadRequest.getSlaveId(), modbusReadRequest.getStart(), modbusReadRequest.getLen(), modbusReadRequest.getType());
                break;
            default:
                log.error("参数错误！{}", JSONUtil.toJsonStr(modbusReadRequest));
                break;
        }
        return responseValue;
    }


    /**
     * 写寄存器
     *
     * @Param: ReadCode, master, slaveId, start, writeValue
     * @Return: java.lang.String
     * @Author: Howe
     * @Date: 2025/3/7 下午7:00
     **/
    public String writeRegister(String ReadCode, ModbusMaster master, int slaveId, int start, String writeValue) {

        String responseValue = null;
        switch (ReadCode) {
            case "05":
                responseValue = Modbus4jRTUReadUtils.writeCoil(master, slaveId, start, Boolean.getBoolean(writeValue));
                break;
            case "06":
                responseValue = Modbus4jRTUReadUtils.writeRegister(master, slaveId, start, Integer.parseInt(writeValue));
                break;
            case "15":
                boolean[] booleanValueArray = Modbus4jRTUReadUtils.strArrayToBooleanArray(writeValue.split(","));
                responseValue = Modbus4jRTUReadUtils.writeCoils(master, slaveId, start, booleanValueArray);
                break;
            case "16":
                short[] valueArray = Modbus4jRTUReadUtils.strArrayToShortArray(writeValue.split(","));
                responseValue = Modbus4jRTUReadUtils.writeRegisters(master, slaveId, start, valueArray);
                break;
            default:
                log.error("参数错误！");
                break;
        }
        return responseValue;

    }


    /**
     * 获取RTU协议主机(此方法不适用多台服务器)
     *
     * @Param: masterConfig
     * @Return: master
     * @Author: Howe
     * @Date: 2025/3/7 下午5:21
     **/
    public ModbusMaster getRTUMaster(IotMasterConfig masterConfig) {
        ModbusMaster master = ModbusCache.getModbusMasterMap().get(masterConfig.getSerialPort());
        if (master == null) {
            master = RTUMasterUtils.getRTUMaster(masterConfig);
            ModbusCache.getModbusMasterMap().put(masterConfig.getSerialPort(), master);
        }
        return master;
    }


    /**
     * 根据设备的属性下发采集设备命令
     *
     * @Param: collectAttributeParameters
     * @Return: AjaxResult
     * @Author: Howe
     * @Date: 2025/3/14 下午5:37
     **/
    public AjaxResult getDeviceDataByAttribute(IotModbusAttributeOperateParameters iotModbusAttributeOperateParameters) {
        IotDevice iotDevice = redisCache.getCacheObject(CacheConstants.DEVICE_KEY + iotModbusAttributeOperateParameters.getDeviceId());
        if (iotDevice == null) {
            return AjaxResult.error("设备不存在");
        }
        IotProductAttributeModel iotProductAttributeModel = redisCache.getCacheObject(CacheConstants.ATTRIBUTE_KEY + iotModbusAttributeOperateParameters.getAttributeId());
        if (iotProductAttributeModel == null) {
            return AjaxResult.error("属性不存在");
        }
        IotProductConfig productConfig = redisCache.getCacheObject(CacheConstants.PRODUCT_KEY + iotDevice.getProductId());
        if (productConfig == null) {
            return AjaxResult.error("产品不存在");
        }
        ModbusMaster master = getTCPModbusMaster(iotDevice.getGatewayIp(), iotDevice.getGatewayPort());
        if (master == null) {
            return AjaxResult.error("设备连接失败");
        }
        //获取modbus读取对象
        ModbusReadRequest modbusReadRequest = getModbusReadRequest(iotDevice, iotProductAttributeModel, master);
        //去寄存器上读取寄存器的值
        String responseValue = readRegister(modbusReadRequest);
        if (responseValue == null) {
            return AjaxResult.error("读取设备属性失败！");
        }
        //组装设备上报数据对象
        IotDeviceData iotDeviceData = getDeviceData(productConfig, iotDevice, iotProductAttributeModel, responseValue);
        deviceDataService.insertIotDeviceData(iotDeviceData);
        return AjaxResult.success(iotDeviceData);
    }

    /**
     * 根据设备属性下发设置属性命令
     *
     * @Param: null
     * @Return:
     * @Author: Howe
     * @Date: 2025/3/14 下午5:58
     **/
    public AjaxResult setDeviceDataByAttribute(IotModbusAttributeOperateParameters iotModbusAttributeOperateParameters) {
        IotDevice iotDevice = redisCache.getCacheObject(CacheConstants.DEVICE_KEY + iotModbusAttributeOperateParameters.getDeviceId());
        if (iotDevice == null) {
            return AjaxResult.error("设备不存在");
        }
        IotProductAttributeModel iotProductAttributeModel = redisCache.getCacheObject(CacheConstants.ATTRIBUTE_KEY + iotModbusAttributeOperateParameters.getAttributeId());
        if (iotProductAttributeModel == null) {
            return AjaxResult.error("属性不存在");
        }
        IotProductConfig productConfig = redisCache.getCacheObject(CacheConstants.PRODUCT_KEY + iotDevice.getProductId());
        if (productConfig == null) {
            return AjaxResult.error("产品不存在");
        }
        ModbusMaster master = getTCPModbusMaster(iotDevice.getGatewayIp(), iotDevice.getGatewayPort());
        if (master == null) {
            return AjaxResult.error("设备连接失败");
        }
        String readCode = ModbusRegisterTypeEnum.getWriteCodeByType(iotProductAttributeModel.getModbusRegisterType());

        String responseValue = writeRegister(readCode, master, iotDevice.getMachineCode(), iotProductAttributeModel.getModbusRegister(), iotModbusAttributeOperateParameters.getAttributeValue());

        if (responseValue == null) {
            return AjaxResult.error("设置设备属性失败！");
        }
        IotDeviceData iotDeviceData = getDeviceData(productConfig, iotDevice, iotProductAttributeModel, responseValue);
        deviceDataService.insertIotDeviceData(iotDeviceData);
        return AjaxResult.success(responseValue);
    }
}
